<template>
  <div>
    <ul class="dc-scroll">
      <template v-if="curprocess != null" v-for="(item, index) in curprocess.arrCur">
        <li class="split" v-if="split && index > 0"></li>
        <li :class="{
          's-bottom': curprocess.direction[index] == 's-bottom',
          's-top': curprocess.direction[index] == 's-top',
        }" :style="{ transition: curprocess.transitions[index], color: color }">
          <div class="s-on" :style="{ background: bgcolor }">{{ item[0] }}</div>
          <div class="s-hide" :style="{ background: bgcolor }" :class="{
            's-top': item[0] > item[1],
            's-bottom': item[0] <= item[1],
          }">
            {{ item[1] }}
          </div>
        </li>
        <template v-if="decimals != 0 && curprocess.arrCur.length - index - 1 == decimals
          ">
          <li class="split" v-if="split"></li>
          <li class="separator" style="transition: all 1.2s ease 0s" :style="{ color: color }">
            <div class="s-on" :style="{ background: bgcolor }">.</div>
            <!-- <div class="s-hide" >{{separator}}</div> -->
          </li>
        </template>

        <template v-if="curprocess.arrCur.length - index - 1 - decimals != 0 &&
          (curprocess.arrCur.length - index - 1 - decimals) % 3 == 0 &&
          separator
          ">
          <li class="split" v-if="split"></li>
          <li class="separator" style="transition: all 1.2s ease 0s" :style="{ color: color }">
            <div class="s-on" :style="{ background: bgcolor }">
              {{ separator }}
            </div>
            <!-- <div class="s-hide" >{{separator}}</div> -->
          </li>
        </template>
      </template>
    </ul>
  </div>
</template>

<script setup>
import { ref, reactive, defineProps, onMounted, onBeforeUnmount } from "vue";

//外部参数
let option = defineProps({
  effect: { type: String, required: true }, //实现的效果表示
  startnum: { type: Number, required: false, default: 0 }, //要显示的数字
  endnum: { type: Number, required: false, default: 0 }, //截止的数据
  count: { type: Number, required: false }, //数字位数，有几个数据，不足是用0补齐,
  duration: { type: Number, required: false, default: 1000 }, //动画时间
  loop: { type: Boolean, required: false, default: false }, //是否一直循环滚动
  step: { type: Number, required: false, default: 1 }, //每次增加或减少数据时的差值
  separator: { type: String, required: false, default: "" }, //千位分隔符,为空则不显示分隔符
  decimals: { type: Number, required: false, default: 0 }, //要显示的小数位数，为0则展示整数
  autoplay: { type: Boolean, required: false, default: false }, //是否自动开始滚动
  split: { type: Number, required: false, default: 0 }, //是否将数据进行分割展示，设置为true时，需指定背景颜色和字体颜色，不指定的话没有效果
  fontSize: { type: String, required: false, default: "" }, //字体大小
  fontWeight: { type: String, required: false, default: "" }, //是否加粗
  colWidth: { type: String, required: false, default: "10px" }, //每个数字所占的宽度
  colHeight: { type: String, required: false, default: "20px" }, //数字的高度
  color: { type: String, required: false, default: "" }, //字体颜色
  bgcolor: { type: String, required: false, default: "" }, //背景颜色
  radius: { type: String, required: false, default: "" }, //圆角值，split为true时有效
  borderWidth: { type: String, required: false, default: "" }, //边框样式,可实现边框效果
  borderColor: { type: String, required: false, default: "" }, //边框样式,可实现边框效果
  borderStyle: { type: String, required: false, default: "" }, //边框样式,可实现边框效果
});

const emit = defineEmits(["end"]);

//实现效果处理器一,数字随机滚动
const effectprocessauto = function () {
  this.oldData = 0;
  this.newData = 0;
  this.arrOld = []; //上一个数据标记
  this.arrNew = []; //新数据标记

  this.arrCur = reactive([]); //当前展示的数据
  this.direction = reactive([]);
  this.transitions = reactive([]);

  this.timer = null;

  //数据的初始化
  this.init = () => {
    for (let i = 0; i < option.count; i++) {
      this.arrOld.push(0);
      this.arrNew.push(0);
      this.arrCur.push([0, 0]);
    }
    this.styleReturn();
  };

  //获取新的随机数
  this.dataGet = () => {
    let length = "1";
    for (let i = 0; i < option.count; i++) {
      length += "0";
    }
    return Math.floor(Math.random() * (length * 1 - 1));
  };

  this.dataSolve = function () {
    let newData_ = this.dataGet(),
      oldData_ = this.newData;
    this.newData = newData_;
    let arrOld_ = [],
      arrNew_ = [];
    for (let i = 0; i < option.count; i++) {
      arrOld_[option.count - 1 - i] = parseInt(oldData_) % 10;
      arrNew_[option.count - 1 - i] = parseInt(newData_) % 10;
      newData_ = Math.floor(newData_ / 10);
      oldData_ = Math.floor(oldData_ / 10);
      this.transitions[i] = "";
      this.direction[i] = "";
    }
    this.arrOld = arrOld_;
    this.arrNew = arrNew_;
    this.oldData = this.newData;
  };

  this.dataDisplay = () => {
    this.direction.splice(0, this.direction.length);
    for (let i = 0; i < option.count; i++) {
      if (this.arrOld[i] > this.arrNew[i]) {
        this.direction.push("s-bottom");
      } else {
        this.direction.push("s-top");
      }
      this.arrCur[i] = [this.arrOld[i], this.arrNew[i]];
    }
  };

  this.animate = () => {
    this.arrOld.forEach((item, index) => {
      this.transitions[index] = 0.2 * (this.arrOld.length - index) + "s";
    });
  };

  this.styleReturn = () => {
    for (let i = 0; i < option.count; i++) {
      this.arrCur[i] = [this.arrNew[i], this.arrOld[i]];
      this.transitions[i] = "";
      this.direction[i] = "";
    }
  };

  this.run = () => {
    this.styleReturn();
    let that = this;
    setTimeout(function () {
      that.dataSolve();
      that.dataDisplay();
      that.animate();
    }, 100);
  };
  this.start = () => {
    this.init();
    this.run();

    if (option.loop) {
      let that = this;
      this.timer = setInterval(function () {
        that.run();
      }, option.duration);
    } else {
      emit("end", {});
    }
  };

  return this;
};

//实现效果处理器二,数字自动增加到指定值
const effectprocesscompute = function () {
  this.arrOld = []; //上一个数据标记
  this.arrNew = []; //新数据标记

  this.curnum = 0;
  this.curstep = 1;

  this.arrCur = reactive([]); //当前展示的数据
  this.direction = reactive([]);
  this.transitions = reactive([]);

  this.arrOld_ = [];
  this.arrNew_ = [];
  this.arrCur_ = [];

  this.timer = null;

  //数据的初始化
  this.init = () => {
    this.curstep = option.step;
    if (option.decimals != 0 && this.curstep > 0) {
      let stepstr = "0";
      while (stepstr.length < option.decimals - 1) {
        stepstr = "0" + stepstr;
      }
      stepstr = stepstr + "1";
      this.curstep = parseFloat("0." + stepstr);
    }
    let nums = option.startnum + "";
    if (option.startnum < 0) {
      nums = -option.startnum + "";
    }
    if (option.decimals != 0) {
      let numstr = nums.split(".");
      while (numstr.length > 1 && numstr[1].length < option.decimals) {
        numstr[1] = numstr[1] + "0";
      }
      while (numstr[0].length < option.count - option.decimals) {
        numstr[0] = "0" + numstr[0];
      }
      nums = numstr[0] + "." + numstr[1];
    } else {
      while (nums.length < option.count) {
        nums = "0" + nums;
      }
    }
    this.curnum = option.startnum;
    let numsnosplit = nums.replace(".", "");
    for (let i = 0; i < option.count; i++) {
      let value = 0;
      if (numsnosplit.length > i) {
        value = parseInt(numsnosplit.charAt(i));
      }
      this.arrOld.push(value);
      this.arrNew.push(value);
      this.arrCur.push([value, value]);

      this.arrOld_.push(value);
      this.arrNew_.push(value);
      this.arrCur_.push([value, value]);
    }
    this.styleReturn();
  };

  this.dataSolve = function () {
    if (option.endnum) {
      if (option.step < 0) {
        if (this.curnum + this.curstep < option.endnum) {
          //到头了
          this.end();
        }
      } else {
        if (this.curnum + this.curstep > option.endnum) {
          //到头了
          this.end();
        }
      }
    }
    // if(option.startnum==33.666){
    // 	debugger
    // }
    let curvalue = this.curnum + this.curstep;
    this.curnum = this.curnum + this.curstep;
    let weishu = "1";
    for (let i = 0; i < option.decimals; i++) {
      weishu += "0";
    }
    this.curnum = Math.floor(this.curnum * parseInt(weishu)) / parseInt(weishu);
    if (option.step > 0) {
      if (this.curnum < curvalue) {
        this.curnum = this.curnum + this.curstep;
        this.curnum =
          Math.floor(this.curnum.toFixed(option.decimals) * parseInt(weishu)) /
          parseInt(weishu);
      }
    } else {
      if (this.curnum > curvalue) {
        this.curnum = this.curnum + this.curstep;
        this.curnum =
          Math.floor(this.curnum.toFixed(option.decimals) * parseInt(weishu)) /
          parseInt(weishu);
      }
    }
    let nums = this.curnum + "";
    if (this.curnum < 0) {
      nums = -this.curnum + "";
    }

    if (option.decimals != 0) {
      let numstr = nums.split(".");
      while (numstr.length > 1 && numstr[1].length < option.decimals) {
        numstr[1] = numstr[1] + "0";
      }
      while (numstr[0].length < option.count - option.decimals) {
        numstr[0] = "0" + numstr[0];
      }
      nums = numstr[0] + "." + numstr[1];
    } else {
      while (nums.length < option.count) {
        nums = "0" + nums;
      }
    }
    let numsnosplit = nums.replace(".", "");

    this.arrOld_.splice(0, this.arrOld_.length);
    this.arrNew_.splice(0, this.arrNew_.length);
    this.arrCur_.splice(0, this.arrCur_.length);

    this.arrOld.forEach((item, index) => {
      this.arrOld_.push(item);
    });
    this.arrNew.forEach((item, index) => {
      this.arrNew_.push(item);
    });
    this.arrCur.forEach((item, index) => {
      this.arrCur_.push(item);
    });

    this.arrOld.splice(0, this.arrOld.length);
    this.arrNew.splice(0, this.arrNew.length);
    this.arrCur.splice(0, this.arrCur.length);

    for (let i = 0; i < option.count; i++) {
      let value = 0;
      if (numsnosplit.length > i) {
        value = parseInt(numsnosplit.charAt(i));
      }
      this.arrOld.push(value);
      this.arrNew.push(value);
      this.arrCur.push([value, value]);
    }
  };

  this.dataDisplay = () => {
    this.direction.splice(0, this.direction.length);
    for (let i = 0; i < option.count; i++) {
      if (this.arrOld[i] > this.arrNew[i]) {
        this.direction.push("s-bottom");
      } else {
        this.direction.push("s-top");
      }
      this.arrCur[i] = [this.arrOld[i], this.arrNew[i]];
    }
  };

  this.animate = () => {
    this.arrOld.forEach((item, index) => {
      if (this.arrCur[index][0] == this.arrCur_[index][0]) {
        return;
      }
      this.transitions[index] = 0.2 * (this.arrOld.length - index) + "s";
    });
  };

  this.styleReturn = () => {
    for (let i = 0; i < option.count; i++) {
      this.arrCur[i] = [this.arrNew[i], this.arrOld[i]];
      this.transitions[i] = "";
      this.direction[i] = "";
    }
  };

  this.run = () => {
    this.styleReturn();
    let that = this;
    setTimeout(function () {
      that.dataSolve();
      that.dataDisplay();
      that.animate();
    }, 100);
  };
  this.start = () => {
    this.init();
    this.run();

    let that = this;
    this.timer = setInterval(function () {
      that.run();
    }, option.duration);
  };
  this.end = () => {
    //动画结束
    clearInterval(this.timer);
    this.timer = null;
    emit("end", {});
  };
  return this;
};

//实现效果处理器三,数字自动增加到指定数字
const effectprocessfixed = function () {
  this.arrOld = []; //上一个数据标记
  this.arrNew = []; //新数据标记

  this.curnum = 0;
  this.curstep = 1;

  this.arrCur = reactive([]); //当前展示的数据
  this.direction = reactive([]);
  this.transitions = reactive([]);

  this.arrOld_ = [];
  this.arrNew_ = [];
  this.arrCur_ = [];

  this.timer = null;

  this.stattime = 0;

  //数据的初始化
  this.init = () => {
    this.curnum = 0;
    if (option.startnum) {
      this.curnum = option.startnum;
    }
    this.curstep = 1;

    let nums = this.curnum + "";
    if (this.curnum < 0) {
      nums = -this.curnum + "";
    }
    while (nums.length < option.count) {
      nums = "0" + nums;
    }
    let newnums = nums.replace(".", "");

    for (let i = 0; i < option.count; i++) {
      let value = 0;
      if (newnums.length > i) {
        value = parseInt(newnums.charAt(i));
      }
      this.arrOld.push(value);
      this.arrNew.push(value);
      this.arrCur.push([value, value]);

      this.arrOld_.push(value);
      this.arrNew_.push(value);
      this.arrCur_.push([value, value]);
    }
    this.styleReturn();

    if (option.endnum < option.startnum) {
      if (option.curnum - this.endnum != 0) {
        if (option.decimals == 0) {
          this.curstep = Math.ceil(
            (option.endnum - this.curnum) / (option.duration / 30)
          );
        } else {
          this.curstep = parseFloat(
            ((option.endnum - this.curnum) / (option.duration / 30)).toFixed(
              option.decimals
            )
          );
        }
      }
    } else {
      if (option.endnum - this.curnum != 0) {
        if (option.decimals == 0) {
          this.curstep = Math.ceil(
            (option.endnum - this.curnum) / (option.duration / 30)
          );
        } else {
          this.curstep = parseFloat(
            ((option.endnum - this.curnum) / (option.duration / 30)).toFixed(
              option.decimals
            )
          );
        }
      }
    }

    // 目标数 = 循环次数*步长;
    // 总时长 = 循环次数*每次循环的时间;
    // 循环次数=目标数/步长;
    // 总时长 = 目标数/步长 * 每次循环的时间;
    // 每次循环的时间 = 总时长/目标数/步长
    // 剩余数字 * 已用时间/总时间;

    this.stattime = 0;
  };

  this.dataSolve = function () {
    if (option.endnum > option.startnum) {
      if (this.curnum + this.curstep >= option.endnum) {
        this.curnum = option.endnum;
        this.end();
      } else {
        this.curnum = this.curnum + this.curstep;
      }
    } else {
      if (this.curnum + this.curstep <= option.endnum) {
        this.curnum = option.endnum;
        this.end();
      } else {
        this.curnum = this.curnum + this.curstep;
      }
    }

    let nums = this.curnum + "";
    if (this.curnum < 0) {
      nums = -this.curnum + "";
    }
    while (nums.length < option.count) {
      nums = "0" + nums;
    }

    this.arrOld_.splice(0, this.arrOld_.length);
    this.arrNew_.splice(0, this.arrNew_.length);
    this.arrCur_.splice(0, this.arrCur_.length);

    this.arrOld.forEach((item, index) => {
      this.arrOld_.push(item);
    });
    this.arrNew.forEach((item, index) => {
      this.arrNew_.push(item);
    });
    this.arrCur.forEach((item, index) => {
      this.arrCur_.push(item);
    });

    this.arrOld.splice(0, this.arrOld.length);
    this.arrNew.splice(0, this.arrNew.length);
    this.arrCur.splice(0, this.arrCur.length);

    let newnums = nums.replace(".", "");
    for (let i = 0; i < option.count; i++) {
      let value = 0;
      if (newnums.length > i) {
        value = parseInt(newnums.charAt(i));
      }
      this.arrOld.push(value);
      this.arrNew.push(value);
      this.arrCur.push([value, value]);
    }
  };

  this.dataDisplay = () => {
    this.direction.splice(0, this.direction.length);
    for (let i = 0; i < option.count; i++) {
      if (this.arrOld[i] > this.arrNew[i]) {
        this.direction.push("s-bottom");
      } else {
        this.direction.push("s-top");
      }
      this.arrCur[i] = [this.arrOld[i], this.arrNew[i]];
    }
  };

  this.animate = () => {
    this.arrOld.forEach((item, index) => {
      if (this.arrCur[index][0] == this.arrCur_[index][0]) {
        return;
      }
      this.transitions[index] = 0.2 * (this.arrOld.length - index) + "s";
    });
  };

  this.styleReturn = () => {
    for (let i = 0; i < option.count; i++) {
      this.arrCur[i] = [this.arrNew[i], this.arrOld[i]];
      this.transitions[i] = "";
      this.direction[i] = "";
    }
  };

  this.run = () => {
    this.styleReturn();
    let that = this;
    setTimeout(function () {
      that.dataSolve();
      that.dataDisplay();
      that.animate();
    }, 10);
    that.stattime += 30;
    if (option.duration <= that.stattime) {
      //已经结束了
      that.curstep = 0;
    } else {
      if (option.endnum < option.startnum) {
        if (option.decimals == 0) {
          that.curstep = Math.ceil(
            (option.endnum - that.curnum) /
            ((option.duration - that.stattime) / 30)
          );
        } else {
          that.curstep = parseFloat(
            (
              (option.endnum - that.curnum) /
              ((option.duration - that.stattime) / 30)
            ).toFixed(option.decimals)
          );
        }
      } else {
        if (option.decimals == 0) {
          that.curstep = Math.ceil(
            (option.endnum - that.curnum) /
            ((option.duration - that.stattime) / 30)
          );
        } else {
          that.curstep = parseFloat(
            (
              (option.endnum - that.curnum) /
              ((option.duration - that.stattime) / 30)
            ).toFixed(option.decimals)
          );
        }
      }
    }
  };

  this.start = () => {
    this.init();
    this.run();

    let that = this;
    this.timer = setInterval(function () {
      that.run();
    }, 10);
  };
  this.end = () => {
    //动画结束
    clearInterval(this.timer);
    this.timer = null;
    emit("end", {});
  };

  return this;
};

//当前的效果处理器
let curprocess = null;

//识别特效类型
if (option.effect == "auto") {
  //随机数特效
  curprocess = new effectprocessauto();
} else if (option.effect == "compute") {
  //自动增加或减少特效
  curprocess = new effectprocesscompute();
} else if (option.effect == "fixed") {
  //自动增加到指定数据
  curprocess = new effectprocessfixed();
}

//启动效果动画
const start = () => {
  if (curprocess.timer) {
    return;
  }
  curprocess && curprocess.start && curprocess.start();
};

onMounted(() => {
  if (option.autoplay) {
    start();
  }
});
onBeforeUnmount(() => {
  curprocess && curprocess.timer && clearInterval(curprocess.timer);
});

defineExpose({
  start,
});

let borderColWidth = option.colWidth;
let borderColHeight = option.colHeight;
if (option.borderWidth) {
  borderColWidth =
    parseInt(
      borderColWidth
        .replace("vh")
        .replace("vw")
        .replace("px")
        .replace("rem")
        .replace("%")
    ) -
    parseInt(
      option.borderWidth
        .replace("vh")
        .replace("vw")
        .replace("px")
        .replace("rem")
        .replace("%")
    ) -
    2 +
    "px";
  borderColHeight =
    parseInt(
      borderColHeight
        .replace("vh")
        .replace("vw")
        .replace("px")
        .replace("rem")
        .replace("%")
    ) -
    parseInt(
      option.borderWidth
        .replace("vh")
        .replace("vw")
        .replace("px")
        .replace("rem")
        .replace("%")
    ) -
    2 +
    "px";
}
</script>
<style scoped>
.dc-scroll {
		height: v-bind(colHeight);
		overflow: hidden;
		list-style: none;
		margin:0;
		padding:0;
	}
	
	.dc-scroll li {
		width: v-bind(colWidth);
		height: v-bind(colHeight);
		display:inline-block;
		font-size: v-bind(fontSize);
		font-weight: v-bind(fontWeight);
		line-height:v-bind(colHeight);
		text-align: center;
		position: relative;
		margin:0;
		padding:0;
	}
	.dc-scroll li.split{
		width: v-bind(split+'px');
		height: v-bind(colHeight);
		display:inline-block;
	}
	
	.dc-scroll li div {
		position: absolute;
		width: v-bind(borderColWidth);
		height: v-bind(borderColHeight);
		border-radius: v-bind(radius);
		border-style: v-bind(borderStyle);
		border-color: v-bind(borderColor);
		border-width: v-bind(borderWidth);
	}
	
	.dc-scroll .s-top {
		transform: translateY(-100%)
	}
	
	.dc-scroll .s-on {
	}
	
	.dc-scroll .s-bottom {
		transform: translateY(100%)
	}
	.dc-scroll .separator {
		
	}
</style>
